<?php
/*********************************************************************
 * ADVERTISEMENT CLASS FILE
 * 
 * Author: hthieu1110
 * Date: 10/04/2011
 **********************************************************************
 * function __call($methodName, $args):
 *   + magic method is called when we call a method name which does not exist
 *   + we use this method for generate automatically all the base method get/set for all the fields
 *   + if the method name corresponds to field name then call the method 
 *   + if method name does not correspond to field name then ---> exception
 * 
 * function add($args):
 *   + we use it for add an advertisement into db
 *   + if all the required fields are filled then add an advertisement in db
 *   + if not then ---> exeption
 *   + we'll never fill three fields: validatedAt, createdAt, expiredAt
 *   + they'll be filled automatically by the datetime at the moment we call the method
 *
 **********************************************************************/

	require_once dirname(realpath(__FILE__)).'./../config/config.inc.php';
	
	class Advertisement {
    public  $advertisementId = '';
    private $countFoundAdvertisements = 0;
    private $Db;
    private $isViaFetchObject;
    
    # fields array : 'field name' => 'is required'
    private $fields = array(
    	array('name'=>'name', 'required'=>true, 'internal'=>true),
    	array('name'=>'type', 'required'=>true, 'internal'=>true),
    	array('name'=>'password', 'required'=>true, 'internal'=>true),
    	array('name'=>'categoryId', 'required'=>true, 'internal'=>true),
    	array('name'=>'email', 'required'=>true, 'internal'=>true),
    	array('name'=>'title', 'required'=>true, 'internal'=>true),
    	array('name'=>'description', 'required'=>true, 'internal'=>true),
    	array('name'=>'townId', 'required'=>true, 'internal'=>true),
    	array('name'=>'id', 'required'=>false, 'internal'=>true),
    	array('name'=>'telephone', 'required'=>false, 'internal'=>true),
    	array('name'=>'price', 'required'=>false, 'internal'=>true),
    	array('name'=>'createdAt', 'required'=>false, 'internal'=>true),
    	array('name'=>'updatedAt', 'required'=>false, 'internal'=>true),
    	array('name'=>'postedAt', 'required'=>false, 'internal'=>true),
    	array('name'=>'expiredAt', 'required'=>false, 'internal'=>true),
    	
    	# altenative
    	array('name'=>'townCode', 'required'=>false, 'internal'=>false),
    	array('name'=>'townName', 'required'=>false, 'internal'=>false),
    	array('name'=>'categoryName', 'required'=>false, 'internal'=>false),
    );
    
    # data stock the infos when initialize the object
    private $preparedData = array(); 

#--------------------------------------------------------------------------------------   
    public function __construct($advertisementId = '', $isViaFetchObject = false) {
      try {
      	$this->Db = new Db();
        global $_advertisement_fields;
        global $_advertisement_required_fields;
        
        $this->advertisementId = $advertisementId;
        $this->fieldList = $_advertisement_fields;
        $this->isViaFetchObject = $isViaFetchObject;
      
        # get the datas if the request is not via fetch object
        if ($advertisementId != '' && $isViaFetchObject == false) {
      	  $query = sprintf('SELECT
      	  										advertisement.*, 
      	        							town.name as townName,
      	        							town.code as townCode,
															category.name as categoryName 
      	  									FROM advertisement
      	  									INNER JOIN town ON town.id = advertisement.townId
      											INNER JOIN category ON category.id = advertisement.categoryId
      	  									WHERE advertisement.id=%d',
      									    mysql_escape_string($this->advertisementId));

          $result = $this->Db->select($query, 'Advertisement');

          foreach($this->fields as $field) {
            $this->preparedData[$field['name']] = $result->$field['name'];
          }
        }
      
      } catch (Exception $e){
      	throw new Exception('<br/>File   : <b>'.__FILE__.'</b>
      											 <br/>Line   : <b>'.__LINE__.'</b>
      											 <br/>Error  : <i>'.$e->getMessage().'</i>
        									 ');
      }
  }

#--------------------------------------------------------------------------------------   
    public function __toString(){
    	$results = array();
    	
    	foreach ($this->fieldList as $field){
    		$results[] = $field.' : '.call_user_func(array($this, 'get'.ucfirst($field)));
    	}
    	
    	return implode('<br/>',$results);
    }
    
#--------------------------------------------------------------------------------------   
    public function getAttributes(){
    	$query = sprintf('SELECT * FROM advertisement WHERE id=%d',
    	                 mysql_escape_string($this->advertisementId));
    	
    	$result = mysql_query($query);
    	
    	if(!$result) throw new Exception('File : '.__FILE__.'<br />Line : '.__LINE__."<br />Error : Unable to get attributes of advertisement '".$this->advertisementId."'");
    
    	return mysql_fetch_array($result);
    }
    
#--------------------------------------------------------------------------------------   
/**
 * All the base methods get/set based on attributes
 * User __call method
 * 1. If method name is based on one of advertisement attributes then call the method, if not --> exeption
 */
   public function __call($methodName, $args){
     	$action = substr($methodName, 0, 3);
     	$tmp = substr($methodName, 3);
     	$firstChar = substr($tmp, 0, 1);
     	$otherChars = substr($tmp, 1); 
     	
     	$fieldName = strtolower($firstChar).$otherChars;
     	try {
     	  $fieldExists = false;
     	  foreach($this->fields as $field) {
     	    if ($field['name'] == $fieldName) $fieldExists = true;
     	  }

     	  if (!$fieldExists) throw new Exception('File : '.__FILE__.'<br />Line : '.__LINE__."<br />Error : Method $methodName does not exist.");
     	} catch (Exception $e) {
     	  echo $e->getMessage();
     	}
    
     	# if via fetch object then return directly the value
     	if ($this->isViaFetchObject) {
     	  return $this->$fieldName;
     	  
     	# if not via fet object then request to Db
     	} else {
     	  try {
        	if($action == 'get'){
            return $this->preparedData[$fieldName];
        	}elseif($action == 'set'){
        		$query = sprintf('UPDATE advertisement SET %s=\'%s\' WHERE id=%d LIMIT 1',
        		                  mysql_escape_string($fieldName),
    													mysql_escape_string($args[0]),
    											  	mysql_escape_string($this->advertisementId));
        	  $result = $this->Db->update($query);
        	  return $result;
        	}
  			}	catch (Exception $e) {
  				echo $e->getMessage();
  			}
     	}
    }

#--------------------------------------------------------------------------------------
    /**
     * 
     * Get the array of object Advertisement
     * @param array $where Add WHERE,
     *  Ex format:$where = array(
     *   'type'=>$adType,
     * 	 'keyword'=>$formData['keyword'],
     *   'categoryId'=>$categoryId,
     *   'town_name'=>$townName,
     *   'town_code'=>$townCode,
     *   'period'=>$period
     *  );
     * @param array $order
     *  Ex format: $order = array(
     *   'by'=>$orderBy,
     *   'direction'=>$orderDirection,
     *  );
     * @param array $limit
     * Ex format:   $limit = array(
     *   'begin'=>($currentPage-1)*MAX_ADS_PER_PAGE,
     *   'number'=>MAX_ADS_PER_PAGE,
     * );
     */
    public function retrieveAdvertisements($where = array(), $order = array(), $limit = array()) {
      //Ex : where = array('type'=>'sale', 'keyword'=>'mua ban');
      //Ex : order = array('by'=>'type', 'direction'=>'DESC');
      //initialize the query template
      $query = '
      	SELECT
      		SQL_CALC_FOUND_ROWS advertisement.*,
      		town.name as townName,
      		town.code as townCode,
					category.name as categoryName      		
      	FROM advertisement
      	INNER JOIN town ON town.id = advertisement.townId
      	INNER JOIN category ON category.id = advertisement.categoryId
      	__WHERE__
      	__ORDER__
      	__LIMIT__
      ';

      //build the where
      //if a where is empty or is not set then we dont add its expression
      $strWhere = '';
      if (isset($where['type'])) $strWhere .= "  (advertisement.type='".mysql_escape_string($where['type'])."') ";
      if (isset($where['categoryId'])) $strWhere .= " AND (advertisement.categoryId IN (".mysql_escape_string($where['categoryId']).")) ";
      if (isset($where['townId'])) $strWhere .= " AND (advertisement.townId='".mysql_escape_string($where['townId'])."') ";
      if (isset($where['keyword']) && !empty($where['keyword'])) $strWhere .= " AND ((advertisement.title LIKE '%".mysql_escape_string($where['keyword'])."%') OR (advertisement.description LIKE '%".mysql_escape_string($where['keyword'])."%')) ";
      if (isset($where['townName']) && !empty($where['townName'])) $strWhere .= " AND town.name LIKE '".mysql_escape_string($where['townName'])."%' ";
      if (isset($where['townCode']) && !empty($where['townCode'])) $strWhere .= " AND town.code LIKE '".mysql_escape_string($where['townCode'])."%' ";
      if (isset($where['period']) && !empty($where['period'])) $strWhere .= " AND createdAt >= '".mysql_escape_string($where['period'])."'";

      $strWhere = ($strWhere != '') ? 'WHERE '.ltrim($strWhere, 'AND') : '';
      $query = str_replace('__WHERE__', $strWhere, $query);
      
      # select the town and category data
      
      
      //build order
      $strOrder = '';
      if(isset($order['by'])) $strOrder .= ' ORDER BY '.mysql_escape_string($order['by']);
      if(isset($order['direction'])) $strOrder .= ' '.strtoupper(mysql_escape_string($order['direction']));
      $query = str_replace('__ORDER__', $strOrder, $query);
      
      //build limit
      $strLimit = '';
      if(!empty($limit)) $strLimit .= ' LIMIT '.mysql_escape_string($limit['begin']).','.mysql_escape_string($limit['number']);
      $query = str_replace('__LIMIT__', $strLimit, $query);

      try {
        $advertisements = $this->Db->selectTable($query, 'Advertisement');

        $queryCountRows = "SELECT FOUND_ROWS( )";
        $countRows = $this->Db->select($queryCountRows);

        $this->countFoundAdvertisements = $countRows['FOUND_ROWS( )'];
        return $advertisements;
      } catch (Exception $e) {
        throw new Exception('<br/>File : <b>'.__FILE__.'</b><br/>Line : <b>'.__LINE__.'</b><br/>Error : <i>'.$e->getMessage().'</i>');
      }
      
      return false;
    }

#--------------------------------------------------------------------------------------
/**
 * Count the total of found advertisements retriveAdvertisement without LIMIT
 * Enter description here ...
 */
    public function countFoundAdvertisements(){
      return $this->countFoundAdvertisements;
    }
    
#--------------------------------------------------------------------------------------
		public function add($args){
		  //add time stamp
      $args['createdAt'] = date('Y-m-d H:i:s');
      
      # Separate the photos from $args
      if (isset($args['photo'])) {
        $photosData = $args['photo'];
        unset($args['photo']);
      }
      
      # clean datas
      $cleanedFields = array();
      foreach ($this->fields as $field) {
        # pass all the external fields
        if ($field['internal']==false) continue;
        
        $fieldValue = mysql_escape_string($args[$field['name']]);
        $fieldValue = str_replace('\r\n', "", $fieldValue);
        $fieldValue = "'".$fieldValue."'";
        
        $cleanedFields[$field['name']] = $fieldValue;
      }

		  # Check if all the required fields are filled
		  $required = array();
      foreach($this->fields as $field){
        if ($field['required']==true && !isset($cleanedFields[$field['name']])) {
           $required[] = $field['name'];
        }
      }

      if(!empty($required))
      	throw new Exception('<br />File : '.__FILE__.'<br />Line : '.__LINE__.'<br />Error : "'.implode(',', $required).'" is required');

      $fieldNames = implode(',', array_keys($cleanedFields));
      $fieldValues = implode(',', array_values($cleanedFields));

      $query = sprintf("INSERT INTO advertisement ($fieldNames) VALUES($fieldValues)");

      try {
      	$result = $this->Db->insert($query);

      } catch (Exception $e) {
      	throw new Exception('<br />File : '.__FILE__.'<br />Line : '.__LINE__.'<br />Error : '.$e->getMessage());
      }
      
      //if all the infos are added into DB successfully, then save the photos
      if(isset($photosData) && !empty($photosData)) {
        $advertisementId = $result;
        $photoPath = PHOTOS_PATH.'advertisement/'.$advertisementId.'/';

        if(is_dir($photoPath)){
          error_log(date('Y-m-d H:i:s').' === File : '.__FILE__.' === Line : '.__LINE__.' === Error : The advertisement photos folder "'.$advertisementId.'" exists'."\n", 3, ERROR_LOG_PATH);
          throw new Exception('<br />File : '.__FILE__.'<br />Line : '.__LINE__.'<br />Error : The advertisement photos folder "'.$advertisementId.'" exists');
        } 
        
        $oldumask = umask(0);
        mkdir($photoPath, 0777);
        umask($oldumask);
        
        foreach($photosData as $i=>$photo) {
          list($dataType, $dataEncoded) = explode(";", $photo);
          list($tmp, $photoType) = explode(":", $dataType);
          list($tmp, $photoDataEncoded) = explode(",", $dataEncoded);
          
          if(trim($photoType) == 'image/jpg' || trim($photoType) == 'image/jpeg') $photoType = 'jpeg';
          elseif(trim($photoType) == 'image/png') $photoType = 'png';
          else $photoType = 'jpg';
          
          if (!file_put_contents("${photoPath}tmp_$i.$photoType", base64_decode($photoDataEncoded))) throw new Exception('<br />File : '.__FILE__.'<br />Line : '.__LINE__.'<br />Error : Cannot create the temp photo "'.$i.'"');
          
          imageGenImage("${photoPath}tmp_$i.$photoType", $photoType, "${photoPath}thumb_$i.jpg", 'jpeg', THUMB_WIDTH, THUMB_HEIGHT);
          imageGenImage("${photoPath}tmp_$i.$photoType", $photoType, "${photoPath}photo_$i.jpg", 'jpeg', PHOTO_WIDTH, PHOTO_HEIGHT);
          
          unlink("${photoPath}tmp_$i.$photoType");
        }
      }
      
      $this->advertisementId = $result;
      return $result;
		}
		
#--------------------------------------------------------------------------------------
		public function update($args){
      //add time stamp
      $args['updated_at'] = date('Y-m-d H:i:s');
      
      $fieldValues = array_values($args);
      $fieldNames = array_keys($args);
      
      //escape values
      array_walk($fieldValues, 'stringEscapeString');

      //add apostrophe
      array_walk($fieldValues, 'stringAddApostrophe');
      
		  //check if all the required fields are filled
		  $required = array();
      foreach($this->requiredFieldList as $requiredField){
      	if(!in_array($requiredField, $fieldNames) || $args[$requiredField] == ''){
      		$required[] = $requiredField;
      	}
      }
      if(!empty($required)){
      	throw new Exception('<br />File : '.__FILE__.'<br />Line : '.__LINE__.'<br />Error : "'.implode(',', $required).'" is required');
      }
			
      $fields = array_combine($fieldNames, $fieldValues);
      
      $assignments = "";
      
      foreach($fields as $name=>$value){
      	$assignments .= ",$name=$value";
      }

      $assignments = substr($assignments, 1);
      
      $query = sprintf('UPDATE advertisement SET %s WHERE id=%d',
                       $assignments,
                       mysql_escape_string($this->advertisementId));

      try {
      	$result = Db::execQuery('update', $query);
      } catch (Exception $e) {
      	throw new Exception('<br />File : '.__FILE__.'<br />Line : '.__LINE__.'<br />Error : '.$e->getMessage());
      }
      
      return $result;
		}
		
#--------------------------------------------------------------------------------------
		/**
		 * Get the advertisement photos
		 */
		static public function retrievePhotos($advertisementId) {
		  $photos = array();
		  #if the folder do not exist then no photo
		  if(!is_dir(PHOTOS_PATH.'advertisement/'.$advertisementId)) {
		    $photos[] = PHOTOS_URL.'advertisement/'.'noPhoto.jpg';
		  
		  } else {
        if ($dirHandler = opendir(PHOTOS_PATH.'advertisement/'.$advertisementId)) {
          $isFolderEmpty = true;
          
          while (($file = readdir($dirHandler)) !== false) {
            if ($file == '.' || $file == '..') continue;
            if(strpos($file, 'thumb_')!==false) continue;
            
            $isFolderEmpty = false;
            $photos[] = PHOTOS_URL.'advertisement/'.$advertisementId.'/'.$file;
          }
          closedir($dirHandler);
          
          #delete folder if it's empty
          if ($isFolderEmpty) {
            rmdir(PHOTOS_PATH.'advertisement/'.$advertisementId);
            $photos[] = PHOTOS_URL.'advertisement/'.'noPhoto.jpg';
          }
        }
		  }

		  #sort the photos
		  usort($photos, array('Advertisement', 'sortNamePhoto'));

		  return $photos;
		}
		
#--------------------------------------------------------------------------------------
		/**
		 * Get the advertisement photos
		 * @param string $advertisementId
		 */
		static public function retrieveThumbs($advertisementId) {
		  $thumbs = array();
		  #if the folder do not exist then no thumb
		  if(!is_dir(PHOTOS_PATH.'advertisement/'.$advertisementId)) {
		    $thumbs[] = PHOTOS_URL.'advertisement/'.'noThumb.jpg';
		  
		  } else {
        if ($dirHandler = opendir(PHOTOS_PATH.'advertisement/'.$advertisementId)) {
          $isFolderEmpty = true;
          
          while (($file = readdir($dirHandler)) !== false) {
            if ($file == '.' || $file == '..') continue;
            if(strpos($file, 'photo_')!==false) continue;
            
            $isFolderEmpty = false;
            $thumbs[] = PHOTOS_URL.'advertisement/'.$advertisementId.'/'.$file;
          }
          closedir($dirHandler);
          
          #delete folder if it's empty
          if ($isFolderEmpty) {
            rmdir(PHOTOS_PATH.'advertisement/'.$advertisementId);
            $thumbs[] = PHOTOS_URL.'advertisement/'.'noThumb.jpg';
          }
        }
		  }
		  
		  #sort the thumbs
		  usort($thumbs, array('Advertisement', 'sortNameThumb'));

		  return $thumbs;
		}

#--------------------------------------------------------------------------------------
		static function sortNamePhoto($name1, $name2) {
		  	preg_match('|advertisement\/[0-9]+\/photo_([0-9]+)\.jpg|', $name1, $matched1);
				preg_match('|advertisement\/[0-9]+\/photo_([0-9]+)\.jpg|', $name2, $matched2);

				return $matched1[1] - $matched2[1];
		}
		
	#--------------------------------------------------------------------------------------
		static function sortNameThumb($name1, $name2) {
		  	preg_match('|advertisement\/[0-9]+\/thumb_([0-9]+)\.jpg|', $name1, $matched1);
				preg_match('|advertisement\/[0-9]+\/thumb_([0-9]+)\.jpg|', $name2, $matched2);

				return $matched1[1] - $matched2[1];
		}
  }